#include "worldbuilder.h"
#include "mapbuilder.h"
#include "roombuilders.h"
#include "random.h"
#include "objectdata.h"
#include "potiondata.h"
#include "runedata.h"
#include "ringdata.h"
#include "miscdata.h"
#include "armourdata.h"
#include "helmdata.h"
#include "glovesdata.h"
#include "bootsdata.h"
#include "monsterdata.h"
#include "map.h"
#include <fstream>

bool WorldBuilder::Build( Game& game, MapList& output )
{
    Generator< Item > itemGen, runeGen, armourGen, miscGen;
    Generator< Actor > actorGen;
    actorGen.AddType< Bat >                 ( 1 );
    actorGen.AddType< Brazier >             ( 1 );
    actorGen.AddType< Crate >               ( 1 );
    miscGen.AddType< HealingPotion >        ( 2 );
    miscGen.AddType< ManaPotion >           ( 2 );
    miscGen.AddType< Torch >                ( 6 );
    miscGen.AddType< BoxOfMatches >         ( 1 );
    armourGen.AddType< LeatherCap >         ( 1 );
    armourGen.AddType< LeatherVest >        ( 1 );
    armourGen.AddType< LeatherGloves >      ( 1 );
    armourGen.AddType< LeatherSandals >     ( 1 );
    armourGen.AddType< RingStr >            ( 1 );
    armourGen.AddType< RingDex >            ( 1 );
    armourGen.AddType< RingInt >            ( 1 );
    runeGen.AddType< SightRune >            ( 1 );
    runeGen.AddType< FarRune >              ( 1 );
    runeGen.AddType< InvisibleRune >        ( 1 );
    runeGen.AddType< FireRune >             ( 1 );
    runeGen.AddType< LightRune >            ( 1 );
    runeGen.AddType< SelfRune >             ( 1 );
    runeGen.AddType< MatterRune >           ( 1 );
    runeGen.AddType< DestroyRune >          ( 1 );
    runeGen.AddType< ProtectRune >          ( 1 );
    runeGen.AddType< KnowledgeRune >        ( 1 );
    runeGen.AddType< ControlRune >          ( 1 );
    itemGen.AddGenerator( &runeGen,   3 );
    itemGen.AddGenerator( &armourGen, 2 );
    itemGen.AddGenerator( &miscGen,   3 );

    try {
        for ( uint i = 0; i < 6; ++i ) {
            Map* map;
            const uint size = i == 0 ?  500 + Random::Int( 200 )
                            : i <  3 ?  300 + Random::Int( 100 )
                            : i <  5 ?  500 + Random::Int( 200 )
                            :          1500 + Random::Int( 500 );

            if ( i == 0 )
                map = BuildOutside( game, size );
            else if ( i < 4 )
                map = BuildCave( game, size );
            else
                map = BuildDungeon( game, size );
            output.push_back( map );

            int numActors = size / 120 + Random::Int( size / 400 );
            int numItems  = size /  80 + Random::Int( size / 240 );
            int x, y;
            if ( i == 0 ) {
                map->GetFreeTile( x, y );
                Item* it = new Torch();
                it->SetMap( map, x, y );
                map->GetFreeTile( x, y );
                it = new BoxOfMatches();
                it->SetMap( map, x, y );
            }
            for ( int j = 0; j < numActors && i > 0; ++j ) {
                map->GetFreeTile( x, y );
                Actor* a = actorGen.Generate();
                a->SetMap( map, x, y );
            }
            for ( int j = 0; j < numItems; ++j ) {
                map->GetFreeTile( x, y, true );
                Item* it = itemGen.Generate();
                it->SetMap( map, x, y );
            }
        }
    }
    catch ( std::exception& e ) {
        std::ofstream f( "error.txt" );
        f << e.what();
        f.close();
        return false;
    }
    for ( uint i = 0; i < output.size() - 1; ++i )
        BuildStairs( *output[ i ], *output[ i + 1 ] );
    return true;
}

Map* WorldBuilder::BuildOutside( Game& game, uint size )
{
    MapBuilder builder;
    builder.AddRoomBuilder< RBPassage >      (  1 );
    builder.AddRoomBuilder< RBBoxRoom >      ( 16 );
    builder.AddRoomBuilder< RBSubRoom >      (  0 );
    builder.AddRoomBuilder< RBPillarRoom >   (  0 );
    builder.AddRoomBuilder< RBAutomataCave > (  8 );
    builder.AddRoomBuilder< RBOrganicCave >  (  8 );
    builder.AddRoomBuilder< RBLabyrinth >    (  0 );
    builder.AddRoomBuilder< RBLabyrinthCave >(  0 );

    Map* map = builder.BuildMap( game, size, 0.9 );
    map->SetType( Map::BARRENS );
    return map;
}

Map* WorldBuilder::BuildCave( Game& game, uint size )
{
    MapBuilder builder;
    builder.AddRoomBuilder< RBPassage >      ( 16 );
    builder.AddRoomBuilder< RBBoxRoom >      ( 16 );
    builder.AddRoomBuilder< RBSubRoom >      ( 16 );
    builder.AddRoomBuilder< RBPillarRoom >   (  2 );
    builder.AddRoomBuilder< RBAutomataCave > ( 32 );
    builder.AddRoomBuilder< RBOrganicCave >  ( 16 );
    builder.AddRoomBuilder< RBLabyrinth >    (  0 );
    builder.AddRoomBuilder< RBLabyrinthCave >(  1 );

    Map* map = builder.BuildMap( game, size, 0.6 );
    map->SetType( Map::CAVERN );
    return map;
}

Map* WorldBuilder::BuildDungeon( Game& game, uint size )
{
    MapBuilder builder;
    builder.AddRoomBuilder< RBPassage >      ( 64 );
    builder.AddRoomBuilder< RBBoxRoom >      ( 32 );
    builder.AddRoomBuilder< RBSubRoom >      ( 16 );
    builder.AddRoomBuilder< RBPillarRoom >   (  4 );
    builder.AddRoomBuilder< RBAutomataCave > (  2 );
    builder.AddRoomBuilder< RBOrganicCave >  (  2 );
    builder.AddRoomBuilder< RBLabyrinth >    (  1 );
    builder.AddRoomBuilder< RBLabyrinthCave >(  1 );

    Map* map = builder.BuildMap( game, size, 0.8 );
    map->SetType( Map::DUNGEON );
    return map;
}

void WorldBuilder::BuildStairs( Map& above, Map& below ) const
{
    int ax, ay, bx, by;
    above.GetFreeTile( ax, ay, true );
    below.GetFreeTile( bx, by, true );
    Stairs* down = new Stairs( true, below,  bx, by );
    Stairs* up   = new Stairs( false, above, ax, ay );
    down->SetMap( &above, ax, ay );
      up->SetMap( &below, bx, by );
}